创建对象
工厂模式
function createPerson(name, age,job){
var o = new object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
}
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");
工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题(即怎样知道一个对象的类型)。
构造函数模式
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = function(){
console.log(this.name);
}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
构造函数的问题
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = new Function("console.log(this.name)"); // 与声明函数在逻辑上是等价的
}
以这种方法创建函数,会导致不同的作用域链和标示符解析。不同实例上的同名函数是不相等的。
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
console.log(person1.sayName == person2.sayName); // false
然后,创建两个完成同样任务的Function实例的确没有必要;况且有this对象在,根本不用在执行代码前就把函数绑定到特定对象上面。因此,大可像下面这样,通过把函数定义转移到构造函数外部来解决这个问题。
function Person(name, age,job){
this.name = name;
this.age = age;
this.job = job;
this.sayName = sayName;
}
function sayName(){
console.log(this.name);
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
可是新问题又来了:在全局作用域中定义的函数实际上只能被某个对象调用,这让全局作用域有点名不副实。而更让人无法接受的是:如果对象需要定义很多方法,那么就要定义很多多个全局函数,于是我们这个自定义的引用类型就丝毫没有封装性可言了。好在,这些问题可以通过使用原型模式来解决。
原型模式
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
}
var person1 = new Person();
person1.sayName(); // Nicholas
var person2 = new Person();
person2.sayName(); // Nicholas
console.log(person1.sayName == person2.sayName);
isPrototypeOf()
console.log(Person.prototype.isPrototypeOf(person1)); // true
console.log(Person.prototype.isPrototypeOf(person2)); // true
hasOwnProperty()
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.hasOwnProperty("name")); // false
person1.name = "Greg";
console.log(person1.name); // Greg
console.log(person1.hasOwnProperty("name")); // true
console.log(person2.name); // Nicholas
console.log(person2.hasOwnProperty("name")); // false
delete person1.name;
console.log(person1.name); // Nicholas
console.log(person1.hasOwnProperty("name")); // false
原型与in操作符
function Person(){}
Person.prototype.name = "Nicholas";
Person.prototype.age = 29;
Person.prototype.job = "Software Engineer";
Person.prototype.sayName = function(){
console.log(this.name);
}
var person1 = new Person();
var person2 = new Person();
console.log(person1.hasOwnProperty("name")); // false
console.log("name" in person1); // true
person1.name = "Greg";
console.log(person1.name); // Greg
console.log(person1.hasOwnProperty('name')); // true
console.log("name" in person1); // true
console.log(person2.name); // Nicholas
console.log(person2.hasOwnProperty('name')); // false
console.log("name" in person2); // true
delete person1.name;
console.log(person1.name); // Nicholas
console.log(person1.hasOwnProperty('name')); // false
console.log("name" in person1); // true
同时使用hasOwnProperty()方法和in操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中,如下:
function hasPrototypeProperty(object,name){
return !object.hasOwnProperty(name)&&(name in object);
}
只要in操作符返回true而hasOwnProperty()返回false,就可以确定属性是原型中的属性。
更简单的原型语法
function Person(){}
Person.prototype = {
name: "Nicholas",
age:29,
job: "Software Engineer",
sayName: function(){
console.log(this.name);
}
}
var friend = new Person();
console.log(friend instanceof Object); // true
console.log(friend instanceof Person); // true
console.log(friend.constructor == Person); // false
console.log(friend.constructor == Object); // true
如果constructor的值真的很重要,可以像下面这样特意将它设置回适当的值。
function Person(){}
Person.prototype = {
constructor: Person,
name: "Nicholas",
age:29,
job: "Software Engineer",
sayName: function(){
console.log(this.name);
}
}
原型对象的问题
function Person(){}
Person.prototype = {
constructor: Person,
name: "Nicholas",
age:29,
job: "Software Engineer",
friends: ['Shelby', "Court"],
sayName: function(){
console.log(this.name);
}
}
var person1 = new Person();
var person2 = new Person();
person1.friends.push("Van");
console.log(person1.friends); //Shelby,Court,Van
console.log(person2.friends); //Shelby,Court,Van
console.log(person1.friends===person2.friends); // true
假如我们的初衷就是像这样在所有实例中共享一个数组,那么对这个结果无话可说。可是,实例一般都是要有属于自己的全部属性的。而这个问题正是我们很少看到有人单独使用原型模式的原因所在。
组合使用构造函数模式和原型模式
function Person(name,age,job){
this.name = name;
this.age = age;
this.job = job;
this.friends = ["Shelby", "Court"];
}
Person.prototype = {
constructor: Person,
sayName: function(){ console.log(this.name);}
}
var person1 = new Person("Nicholas", 29, "Software Engineer");
var person2 = new Person("Greg", 27, "Doctor");
person1.friends.push("Van");
console.log(person1.friends); // Shelby, Count, Van
console.log(person2.friends); // Shelby, Count
console.log(person1.friends === person2.friends); // false
console.log(person1.sayName === person2.sayName); // true
在这个例子中,实例属性都是在构造函数中定义的,而由所有实例共享的属性constructor和方法sayName()则是在原型中定义的。这种构造函数与原型混成的模式,是目前认同度最高的一种创建自定义类型的方法。
动态原型模式
function Person(name, age,job){
this.name = name;
this.age = age;
this.job = job;
}
if (typeof this.sayName!='function'){
Person.prototype.sayName = function(){
console.log(this.name);
}
}
var friend = new Person("Nicholas",29,"Software Engineer");
friend.sayName(); //Nicholas
寄生构造函数模式
function Person(name,age,job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
console.log(this.name);
};
return o;
}
var friend = new Person("Nicholas", 29, "Software Engineer");
friend.sayName(); // Nicholas
关于寄生构造函数模式,返回的对象与构造函数或者构造函数的原型属性之间没有关系;也就是说,构造函数返回的对象与在构造函数外部创建的对象没有什么不同。
继承
原型链
function SuperType(){
this.property= true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function Subtype(){
this.subproperty = false;
}
// 继承了SuperType
Subtype.prototype = new SuperType();
Subtype.prototype.getSubValue = function(){
return this.subproperty;
}
var instance = new Subtype();
console.log(instance.getSuperValue()); // true
谨慎地定义方法
function SuperType(){
this.property= true;
}
SuperType.prototype.getSuperValue = function(){
return this.property;
};
function Subtype(){
this.subproperty = false;
}
// 继承了SuperType
Subtype.prototype = new SuperType();
Subtype.prototype = {
getSubValue: function(){
return this.subproperty;
},
someOtherMethod: function(){
return false;
}
};
var instance = new Subtype();
console.log(instance.getSuperValue()); // error
原型链的问题
- 包含引用类型值的原型属性会被所有实例共享;而这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。
- 在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上,应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。
借用构造函数
function SuperType(){
this.colors = ["red", "blue", "green"];
}
function Subtype(){
SuperType.call(this);
}
var instance1 = new Subtype();
instance1.colors.push("black");
console.log(instance1.colors); // red, blue, green, black
var instance2 = new Subtype();
console.log(instance2.colors); // red, blue, green
传递参数
function SuperType(name){
this.name = name;
}
function Subtype(){
SuperType.call(this,"Nicholas");
this.age = 29;
}
var instance = new Subtype();
console.log(instance.name); //Nicholas
console.log(instance.age); // 29
组合继承
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
};
function Subtype(name,age){
SuperType.call(this,name);
this.age = age;
}
Subtype.prototype = new SuperType();
Subtype.prototype.sayAge = function(){
console.log(this.age);
};
var instance1 = new Subtype("Nicholas", 29);
instance1.colors.push("black");
console.log(instance1.colors); // red, blue, green, black
instance1.sayName(); // Nicholas
instance1.sayAge(); //29
var instance2 = new Subtype("Greg", 2);
console.log(instance2.colors); // red, blue, green
instance2.sayName(); // Greg
instance2.sayAge(); //2
组合继承避免了原型链和借用函数的缺陷,融合了它们的优点,成为Javascript中最常用的继承模式。
原型式继承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
var person = {
name:"Nicholas",
friends:["Shelby", "Court", "Van"]
};
var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // Shelby, Court, Van, Rob, Barbie
Object.create()
Object.create()方法规范了原型式继承。
var person = {
name:"Nicholas",
friends:["Shelby", "Court", "Van"]
};
var anotherPerson = Object.create(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");
var yetAnotherPerson = Object.create(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");
console.log(person.friends); // Shelby, Court, Van, Rob, Barbie
寄生式继承
function object(o){
function F(){}
F.prototype = o;
return new F();
}
function inheritPrototype(subType,superType){
var prototype = object(superType.prototype);
prototype.constructor = subType;
subType.prototype = prototype;
}
function SuperType(name){
this.name = name;
this.colors = ["red", "blue", "green"];
}
SuperType.prototype.sayName = function(){
console.log(this.name);
}
function Subtype(name,age){
SuperType.call(this,name);
this.age = age;
}
inheritPrototype(Subtype, SuperType);
Subtype.prototype.sayAge = function(){
console.log(this.age);
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。